package cn.ante.basic.service.impl;

import cn.ante.basic.exception.BusinessException;
import cn.ante.basic.service.IVerifyCodeService;
import cn.ante.basic.utils.MailUtil;
import cn.ante.basic.utils.StrUtils;
import cn.ante.basic.utils.VerifyCodeUtils;
import cn.ante.basic.vo.BinderVo;
import cn.ante.basic.vo.EmailRegisterDto;
import cn.ante.basic.vo.RegisterVo;
import cn.ante.basic.constant.BaseContants;
import cn.ante.user.domain.User;
import cn.ante.user.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

@Service
public class VerifyCodeServiceImpl implements IVerifyCodeService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Override
    public String graph(String key) {
        // 判断key是否为空
        if (StringUtils.isEmpty(key)){
            throw new BusinessException("请传递参数");
        }
        // 调用工具类获得四位随机数
        String code = StrUtils.getComplexRandomString(4);
        // 将随机数存到redis中.key就是传递过来的key，设置存图形的时间为5分钟
        redisTemplate.opsForValue().set(key,code,5, TimeUnit.MINUTES);

        // 将code生成图形并编码成base64
        String base64Str = VerifyCodeUtils.VerifyCode(100, 35, code);

        // 返回编码好的字符串
        return base64Str;
    }

    @Override
    public void smsCode(RegisterVo vo) {
        String imageCodeValue = vo.getImageCodeValue();
        String imageCodeKey = vo.getImageCodeKey();
        String phone = vo.getPhone();
        
        // 判断参数是否为空
        if (StringUtils.isEmpty(imageCodeValue) || StringUtils.isEmpty(phone) 
                ||StringUtils.isEmpty(imageCodeKey)){
            throw new BusinessException("请输入正确参数");
        }
        // 继续进行注册，判断图形验证码是否存在redis中并且正确
        // 取出redis中的验证码
        Object imageCodeValueTmp = redisTemplate.opsForValue().get(imageCodeKey);
        // 判断得到的redis的验证码是否为空
        // 为空则表示，redis中没有值，抛出异常
        if (imageCodeValueTmp == null){
            throw new BusinessException("验证码已过期，请输入");
        }
        // 如果存在，需要判断这个验证码与前端传入的验证码是否一致
        if (!imageCodeValueTmp.toString().equalsIgnoreCase(imageCodeValue)){
            throw new BusinessException("验证码错误，请输入正确验证码");
        }

        // 判断手机号是否注册
        User user = userMapper.loadByPhone(phone);
        if (user != null){
            throw new BusinessException("用户已注册，请直接登录");
        }
        // 当图形验证码正确时，进行手机验证码
        // 使用业务键拼接手机号得到redis中的key，然后使用方法得到redis中key的值value获取手机验证码code
        String phoneCodeKey = BaseContants.VerifyCodeConstant.BUSINESS_REGISTER_PREFIX + phone;
        Object phoneCodeValue = redisTemplate.opsForValue().get(phoneCodeKey);

        String phoneCode = "";  // 定义一个空字符串来存取发送给用户的短信验证码
        // 判断从redis中取得的短信验证码的值，是否为空.为空则说明手机号不对，或者验证码三分钟已过
        if (phoneCodeValue == null){
            // 生成新的验证码
            phoneCode = StrUtils.getComplexRandomString(4);
        }else{
            // 如果在redis中能获得验证码code,则表示验证码还未过期，手机号与全段传入的手机号一致
            // 此时需要判断code在redis中的时间是否超过一分钟，没有超过就要抛出异常，超过一分钟就将旧的code重新发送
            // 获取到code存放到reids时的时间戳
            String[] smsCodeValueArray = phoneCodeValue.toString().split(":");
            Long oldTimeTmp = Long.valueOf(smsCodeValueArray[0]);
            // 获取到现在的时间戳
            Long nowTimeTmp = System.currentTimeMillis();
            // 判断两个时间戳之间的之差是否大于一分钟
            // 当时间没有超过一分钟时，提醒用户抛出异常
            if (nowTimeTmp - oldTimeTmp <= 1*60*1000){
                throw new BusinessException("请一分钟之后重试");
            }

            // 超过一分钟，则将存在redis中的code重新发送,根据上面的字符串数组可以拿到
            phoneCode = smsCodeValueArray[1];
        }
        // 都要将手机验证码的value值存入到redis中，使用时间戳+手机验证码的方式，并设置三分钟有效
        String phoneCodeRedis = System.currentTimeMillis() + ":" + phoneCode;
        redisTemplate.opsForValue().set(phoneCodeKey,phoneCodeRedis,3,TimeUnit.MINUTES);
        // 发送短信验证码
        System.out.print("验证码是：" + phoneCode);
    }

    @Override
    public void binderSmsCode(BinderVo binderVo) {
//        String verifyCode = binderVo.getVerifyCode();
//        String type = binderVo.getType();
        String phone = binderVo.getPhone();

        // 非空判断
        if (StringUtils.isEmpty(phone)) {
            throw new BusinessException("请输入参数");
        }
        String binderKey = BaseContants.VerifyCodeConstant.BUSINESS_BINDER_PREFIX + phone;
        Object phoneCodeRedisValue = redisTemplate.opsForValue().get(binderKey);
        String phoneCode = "";  // 定义一个空字符串来存取发送给用户的短信验证码
        // 判断从redis中取得的短信验证码的值，是否为空.为空则说明手机号不对，或者验证码三分钟已过
        if (phoneCodeRedisValue == null){
            // 生成新的验证码
            phoneCode = StrUtils.getComplexRandomString(4);
        }else{
            // 如果在redis中能获得验证码code,则表示验证码还未过期，手机号与全段传入的手机号一致
            // 此时需要判断code在redis中的时间是否超过一分钟，没有超过就要抛出异常，超过一分钟就将旧的code重新发送
            // 获取到code存放到reids时的时间戳
            String[] smsCodeValueArray = phoneCodeRedisValue.toString().split(":");
            Long oldTimeTmp = Long.valueOf(smsCodeValueArray[0]);
            // 获取到现在的时间戳
            Long nowTimeTmp = System.currentTimeMillis();
            // 判断两个时间戳之间的之差是否大于一分钟
            // 当时间没有超过一分钟时，提醒用户抛出异常
            if (nowTimeTmp - oldTimeTmp <= 1*60*1000){
                throw new BusinessException("请一分钟之后重试");
            }

            // 超过一分钟，则将存在redis中的code重新发送,根据上面的字符串数组可以拿到
            phoneCode = smsCodeValueArray[1];
        }
        // 都要将手机验证码的value值存入到redis中，使用时间戳+手机验证码的方式，并设置三分钟有效
        String phoneCodeRedis = System.currentTimeMillis() + ":" + phoneCode;
        redisTemplate.opsForValue().set(binderKey,phoneCodeRedis,3,TimeUnit.MINUTES);
        // 发送短信验证码
        System.out.print("验证码是：" + phoneCode);

    }

    @Override
    public void sendmailCode(RegisterVo vo) {
        // 用户填写的邮箱
        String email = vo.getEmail();
        // 为空判断
        if(StringUtils.isEmpty(email)){
            throw new BusinessException("邮箱不能为空，请重新输入");
        }
        // 正则表达式校验手机号格式是否正确
        if(!Pattern.matches(BaseContants.VerifyShopSettlement.REGEX_EMAIL,email)){
            throw new BusinessException("请输入正确的邮箱,请重新输入");
        }
        // 判断用户是否已经被注册
        User user = userMapper.getByEmail(email);
        if (user != null) {
            throw new BusinessException("用户已被注册！请直接登录或更换用户名");
        }
        // redis中获取对应的value值,判断邮箱验证码是否存在
        String emailCodeKey = BaseContants.VerifyCodeConstant.BUSINESS_REGISTER_PREFIX + email;
        Object emailVerify = redisTemplate.opsForValue().get(emailCodeKey);
        String emailCode = "";
        if (emailVerify == null) {
            // 如果为空，说明是第一次获取，直接生成新的验证码
            emailCode = StrUtils.getComplexRandomString(6);
        }else {
            // 如果不为空，说明redis中存在验证码,需要判断发送短信验证码的时间是否超过一分钟
            // 通过业务键截取到时间戳，判断是否超过一分钟
            String[] tmp = emailVerify.toString().split(":");
            // 获取到老的时间戳
            Long oldTime = Long.valueOf(tmp[0]);
            // 获取当前时间戳
            Long nowTime = System.currentTimeMillis();
            if (nowTime - oldTime <= 60 * 1000) {
                throw new BusinessException("发送时间小于一分钟，请稍后重试！");
            }
            // 如果redis中存在此验证码，那么使用之前的验证码并重置时间戳
            emailCode = tmp[1];
        }
            // 将验证码存在redis中，设置过期时间为三分钟，并拼接上时间戳
            String emailRedisValue = System.currentTimeMillis() + ":" + emailCode;
            redisTemplate.opsForValue().set(emailCodeKey, emailRedisValue, 3, TimeUnit.MINUTES);

//        // 使用业务键拼接手机号从redis中获取手机验证码
//        String emailRedisKey = BaseContants.VerifyCodeConstant.BUSINESS_REGISTER_PREFIX + email;
//        // 从redis中查询
//        Object emailCodeValueRedis = redisTemplate.opsForValue().get(emailRedisKey);
        // 创建一个口的字符串对象用于存放重新发送给用户的验证码

        // 判断验证码是否为空

        MailUtil.sendMail(
                "594390939@qq.com",
                "尊敬的用户,您好:",
                "<br>您正在注册【宠物之家平台】账号，您的验证码为：<br>" + emailCode);
        System.err.println(emailCode);
    }
    }

